﻿using Core.Res;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif

namespace Core
{
    public enum EResMode
    {
        Resource,
        AssetDatabase,
        Bundle,
        WeChat
    }

    public class ResSystem : Component, IAwake, IUpdate
    {
        private readonly Dictionary<int, IAsset> dict = new();

        private ILoader loader;

        //回收站
        private GameObject recycler;

        public void Awake()
        {
            log.d("\tResSystem.Awake");

            switch (Global.ResMode)
            {
                case EResMode.Resource:
                    loader = G.Factory.Create<EditorLoader, EditorLoader.ApiMode>(EditorLoader.ApiMode.Resource);
                    break;
                case EResMode.AssetDatabase:
                    loader = G.Factory.Create<EditorLoader, EditorLoader.ApiMode>(EditorLoader.ApiMode.AssetDatabase);
                    break;
                case EResMode.Bundle:
                    loader = G.Factory.Create<BundleLoader>();
                    break;
                case EResMode.WeChat:
                    loader = G.Factory.Create<WXLoader>();
                    break;
            }


            if (recycler == null)
            {
                recycler = new GameObject($"{G.Name}_recycler");
                Utils.Trans.SetActive(recycler, false);
                GameObject.DontDestroyOnLoad(recycler);
            }
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

            loader.Dispose();
            loader = null;

            log.d("\tResSystem.Dispose");
        }


        public void Update()
        {
            loader?.Update();
        }

        public IAsset GetAsset(string path)
        {
            return loader.GetAsset(path);
        }

        public async ETTask<IAsset> LoadAsync(string name)
        {
            if (IsDisposed) return null;

            try
            {
                var asset = await loader.LoadAsync(name);
                return asset;
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                return null;
            }
        }

        public async ETTask LoadAsync(IEnumerable<string> paths, Action<ILoadProgress> func = null)
        {
            if (IsDisposed || paths == null) return;

            try
            {
                await loader.LoadAsync(paths, func);
            }
            catch (Exception e)
            {
                log.err(e);
            }
        }

        public IAsset LoadSync(string name)
        {
            if (IsDisposed) return null;

            try
            {
                var asset = loader.LoadSync(name);
                return asset;
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                return null;
            }
        }

        private GameObject NewGameObject(IAsset vo)
        {
            GameObject go;

            try
            {
                go = UnityEngine.Object.Instantiate(vo.Obj) as GameObject;
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                return null;
            }

            if (go == null) return null;

            var id = go.GetHashCode();
            while (dict.ContainsKey(id))
            {
                GameObject.DestroyImmediate(go);
                go = UnityEngine.Object.Instantiate(vo.Obj) as GameObject;
                if (go == null) return null;
                id = go.GetHashCode();
            }

            dict[id] = vo;

            return go;
        }

        private GameObject PopGameObject(IAsset vo)
        {
            GameObject go = null;

            while (vo.Pool.Count > 0)
            {
                go = vo.Pool.Pop();
                if (go == null)
                {
                    continue;
                }

                var id = go.GetHashCode();
                if (!dict.TryAdd(id, vo))
                {
                    continue;
                }

                break;
            }

            return go;
        }

        private GameObject GetGameObject(IAsset vo)
        {
            if (vo == null || vo.Obj == null)
            {
                return null;
            }

            GameObject go;

            if (vo.Pool.Count <= 0)
            {
                go = NewGameObject(vo);
            }
            else
            {
                go = PopGameObject(vo);

                go ??= NewGameObject(vo);
            }

            if (go != null)
            {
                vo.Ref.Increase();
            }

            return go;
        }

        public GameObject GetGameObjectSync(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                return null;
            }

            try
            {
                var vo = loader.GetAsset(path);
                return GetGameObject(vo);
            }
            catch (Exception e)
            {
                log.err(path);
                log.err(e);
                return null;
            }
        }

        public async ETTask<GameObject> GetGameObjectAsync(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                return null;
            }

            try
            {
                var vo = await loader.LoadAsync(path);
                return GetGameObject(vo);
            }
            catch (Exception e)
            {
                log.err(path);
                log.err(e);
                return null;
            }
        }

        public void Recycle(GameObject go, bool destroy = false)
        {
            //if (null != SceneSwitchMgr.Instance.Curr && SceneSwitchMgr.Instance.Curr.IsBattleScene())
            //{
            //    InnerRecycle(go, m_re, destroy);
            //}
            //else
            //{
            //    InnerRecycle(go, m_res_city_container, destroy);
            //}
            InnerRecycle(go, recycler, destroy);
        }

        private void InnerRecycle(GameObject go, GameObject contailer, bool destroy = false)
        {
            if (go == null || contailer == null) return;

            var id = go.GetHashCode();

            dict.TryGetValue(id, out IAsset vo);

            if (vo != null)
            {
                if (destroy == false)
                {
                    //避免重复回收
                    bool result = vo.Pool.Contains(go);
                    //foreach (var item in vo.Pool)
                    //{
                    //    var hash_code = item.GetHashCode();
                    //    if (hash_code == id)
                    //    {
                    //        result = true;
                    //        break;
                    //    }
                    //}

                    if (result == false)
                    {
                        vo.Ref.Decrease();
                        vo.Pool.Push(go);
                        go.SetParent(contailer);
                    }
                }
                else
                {
                    vo.Ref.Decrease();
                    Utils.Trans.Delete(vo.Pool, go);
                    GameObject.DestroyImmediate(go);
                    dict.Remove(id);
                }
            }
            else
            {
                Utils.Trans.SetActive(go, false);
            }
        }


        public void Destroy(GameObject go)
        {
            if (go == null)
            {
                return;
            }

            var id = go.GetHashCode();

            try
            {
                GameObject.DestroyImmediate(go);
                go = null;
            }
            catch (Exception e)
            {
                log.err(e);
            }

            dict.TryGetValue(id, out IAsset vo);
            if (vo != null)
            {
                vo.Ref.Decrease();

                dict.Remove(id);
            }
        }


        private readonly Dictionary<string, string> mAssetPathMap = new();

        protected string GetAssetFullPath(string path)
        {
            if (mAssetPathMap.ContainsKey(path) == false)
            {
                if (!path.StartsWith("Assets"))
                {
                    mAssetPathMap[path] = $"Assets/Proj/Games/{G.Name}/{path}";
                }
                else
                {
                    mAssetPathMap[path] = path;
                }
            }

            return mAssetPathMap[path];
        }

        //        public UnityEngine.Object Load(string path)
        //        {
        //#if UNITY_EDITOR
        //            string full_path = GetAssetFullPath(path);
        //            var obj = AssetDatabase.LoadAssetAtPath(full_path, typeof(UnityEngine.Object));
        //            if (obj == null)
        //            {
        //                log.err($"资源不存在，path={path}");
        //            }
        //            return obj;
        //#else
        //            return null;
        //#endif

        //        }

        public string LoadText(string path)
        {
#if UNITY_EDITOR

            string fullPath = GetAssetFullPath(path);
            var asset = AssetDatabase.LoadAssetAtPath<TextAsset>(fullPath);
            if (asset == null)
            {
                log.err($"文本资源不存在 full_path={fullPath}");
                return string.Empty;
            }

            return asset.text;
#else
            return "";
#endif
        }

        public AsyncOperation LoadSceneAsync(string path, LoadSceneMode mode)
        {
#if UNITY_EDITOR
            string fullPath = GetAssetFullPath(path);
            LoadSceneParameters parm = new() { loadSceneMode = mode };
            return EditorSceneManager.LoadSceneAsyncInPlayMode(fullPath, parm);
#else
            return null;
#endif
        }
    }
}
